package monitor

import (
	"encoding/json"
	"fmt"
	httperror "github.com/portainer/libhttp/error"
	//"github.com/portainer/libhttp/request"
	"github.com/portainer/libhttp/response"
	portainer "github.com/portainer/portainer/api"
	"io/ioutil"
	"net/http"
	"strconv"
	"strings"
	"syscall"
	"time"
)

type MonitorCPUStat struct {
	CpuUsage string `json:"cpuusage"`
	PrevCPUStat portainer.CPUStat
	CurCPUStat portainer.CPUStat
}

type MonitorMEMInfo struct {
	MemUsage string `json:"memusage"`
	MemInfo portainer.MEMInfo
}

type DiskStatus struct {
	All  uint64 `json:"all"`
	Used uint64 `json:"used"`
	Free uint64 `json:"free"`
}

type HostInfo struct {
	DockerVersion string
	//RunningContainerCount
}

type RequestData struct {
	UUID string `json:"UUID"`
}

func (handler *Handler) monitorCPU(w http.ResponseWriter, r *http.Request) *httperror.HandlerError {
	//handler.DataStore.Init()
	fmt.Println("Monitor CPU")

	body, err := ioutil.ReadAll(r.Body)
	if err != nil {
		fmt.Printf("read body err, %v\n", err)
		return &httperror.HandlerError{StatusCode: http.StatusInternalServerError, Message: "read body err", Err: err}
	}
	println("json:", string(body))

	var a RequestData
	if err = json.Unmarshal(body, &a); err != nil {
		fmt.Printf("Unmarshal err, %v\n", err)
		return &httperror.HandlerError{StatusCode: http.StatusInternalServerError, Message: "Unmarshal err", Err: err}
	}
	fmt.Printf("%+v", a)

	//UUID := r.FormValue("UUID")

	prevCpuStat, err := handler.DataStore.CPUMonitor().CPUStatLatest(a.UUID)
	if prevCpuStat == nil {
		cpuStat := getCPUStat(a.UUID)
		//fmt.Println(cpuStat)
		err = handler.DataStore.CPUMonitor().UpdateCPUMonitor(cpuStat)
		if err != nil{
			return &httperror.HandlerError{StatusCode: http.StatusInternalServerError, Message: "Unable to put cpu stat into database", Err: err}
		}
		return response.Empty(w)
	}
	err = handler.DataStore.CPUMonitor().DeleteCPUStat(*prevCpuStat)
	if err != nil {
		fmt.Println(err)
	}
	//fmt.Println(prevCpuStat)
	cpuStat := getCPUStat(a.UUID)
	//fmt.Println(cpuStat)
	err = handler.DataStore.CPUMonitor().UpdateCPUMonitor(cpuStat)
	if err != nil{
		return &httperror.HandlerError{StatusCode: http.StatusInternalServerError, Message: "Unable to put cpu stat into database", Err: err}
	}

	cpuUsagePercent := strconv.FormatFloat(cpuUsage(cpuStat, *prevCpuStat), 'f', 8, 64)

	return response.JSON(w, MonitorCPUStat{CpuUsage: cpuUsagePercent, PrevCPUStat: *prevCpuStat, CurCPUStat: cpuStat})

}

func (handler *Handler) monitorMEM(w http.ResponseWriter, r *http.Request) *httperror.HandlerError {
	//handler.DataStore.Init()
	fmt.Println("Monitor MEM")

	memInfo := getMEMInfo()

	memTotal := float64(memInfo.MemTotal)
	memFree := float64(memInfo.MemFree)
	buffers := float64(memInfo.Buffers)
	cached := float64(memInfo.Cached)

	memUsed := memTotal - memFree - buffers - cached

	memUsage := 100 * (memUsed / memTotal)

	return response.JSON(w, MonitorMEMInfo{MemUsage: strconv.FormatFloat(memUsage, 'f', 8, 64), MemInfo: memInfo})

}


func (handler *Handler) monitorDisk(w http.ResponseWriter, r *http.Request) *httperror.HandlerError {
	//handler.DataStore.Init()
	fmt.Println("Monitor Disk")

	fs := syscall.Statfs_t{}
	err := syscall.Statfs("/var/lib/docker", &fs)
	if err != nil {
		return &httperror.HandlerError{StatusCode: http.StatusInternalServerError, Message: "Get Disk Info Failed", Err: err}
	}
	all := fs.Blocks * uint64(fs.Bsize)
	free := fs.Bfree * uint64(fs.Bsize)
	used := all - free
	return response.JSON(w, DiskStatus{All: all, Free: free, Used: used})

}

func (handler *Handler) monitorHost(w http.ResponseWriter, r *http.Request) *httperror.HandlerError {
	fmt.Println("Monitor Host")

	//endpointID, err := request.RetrieveNumericRouteVariableValue(r, "id")
	//if err != nil {
	//	return &httperror.HandlerError{http.StatusBadRequest, "Invalid endpoint identifier route variable", err}
	//}

	//endpoint, err := handler.DataStore.Endpoint().Endpoint(portainer.EndpointID(endpointID))
	//handler.DataStore.Init()

	//endpoint.Snapshots
	//
	////fs := syscall.Statfs_t{}
	////err := syscall.Statfs("/var/lib/docker", &fs)
	////if err != nil {
	////	return &httperror.HandlerError{StatusCode: http.StatusInternalServerError, Message: "Get Disk Info Failed", Err: err}
	////}
	////all := fs.Blocks * uint64(fs.Bsize)
	////free := fs.Bfree * uint64(fs.Bsize)
	////used := all - free
	//return response.JSON(w, DiskStatus{All: all, Free: free, Used: used})
	return nil
}

//// disk usage of path/disk
//func DiskUsage(path string) (disk DiskStatus) {
//
//}




func getCPUStat(UUID string) (cpuStat portainer.CPUStat) {
	var stat portainer.CPUStat
	contents, err := ioutil.ReadFile("/proc/stat")
	if err != nil {
		return
	}
	lines := strings.Split(string(contents), "\n")
	for _, line := range(lines) {
		fields := strings.Fields(line)
		fmt.Println(fields)
		if fields[0] == "cpu" {
			numFields := len(fields)
			if numFields >= 7{
				//val, err := strconv.ParseUint(fields[i], 10, 64)
				stat.UUID = UUID
				stat.CPUStatTime = uint64(time.Now().UnixNano())
				stat.UserTime, err = strconv.ParseUint(fields[1], 10, 64)
				stat.NiceTime, err = strconv.ParseUint(fields[2], 10, 64)
				stat.SystemTime, err = strconv.ParseUint(fields[3], 10, 64)
				stat.IdleTime, err = strconv.ParseUint(fields[4], 10, 64)
				stat.IowaitTime, err = strconv.ParseUint(fields[5], 10, 64)
				stat.IrqTime, err = strconv.ParseUint(fields[6], 10, 64)
				stat.SoftirqTime, err = strconv.ParseUint(fields[7], 10, 64)
				//cpuStat.UserTime, _ = strconv.ParseUint(fields[1], 10, 64)
				if err != nil{
					fmt.Println("Error: ", err)
				}
				break
			}
		}
	}
	return stat
}

func getMEMInfo() (memInfo portainer.MEMInfo) {
	var info portainer.MEMInfo

	//
	//sysInfo := new(syscall.Sysinfo_t)
	//err := syscall.Sysinfo(sysInfo)

	contents, err := ioutil.ReadFile("/proc/meminfo")
	if err != nil {
		return
	}
	lines := strings.Split(string(contents), "\n")
	i := 0
	for _, line := range(lines) {
		fields := strings.Fields(line)
		fmt.Println(fields)
		if strings.Contains(fields[0], "MemTotal") {
			numFields := len(fields)
			if numFields > 1{
				info.MemTotal, err = strconv.ParseUint(fields[1], 10, 64)
				if err != nil{
					fmt.Println("Error: ", err)
				}
			}
			i += 1
		}
		if strings.Contains(fields[0], "MemFree") {
			numFields := len(fields)
			if numFields > 1{
				info.MemFree, err = strconv.ParseUint(fields[1], 10, 64)
				if err != nil{
					fmt.Println("Error: ", err)
				}
			}
			i += 1
		}
		if strings.Contains(fields[0], "MemAvailable") {
			numFields := len(fields)
			if numFields > 1{
				info.MemAvailable, err = strconv.ParseUint(fields[1], 10, 64)
				if err != nil{
					fmt.Println("Error: ", err)
				}
			}
			i += 1
		}
		if strings.Contains(fields[0], "Buffers") {
			numFields := len(fields)
			if numFields > 1{
				info.Buffers, err = strconv.ParseUint(fields[1], 10, 64)
				if err != nil{
					fmt.Println("Error: ", err)
				}
			}
			i += 1
		}
		if strings.Contains(fields[0], "Cached") {
			numFields := len(fields)
			if numFields > 1{
				info.Cached, err = strconv.ParseUint(fields[1], 10, 64)
				if err != nil{
					fmt.Println("Error: ", err)
				}
			}
			i += 1
		}
		if i == 5{
			break
		}
	}
	return info
}

func cpuUsage(curCpuStat portainer.CPUStat, prevCpuStat portainer.CPUStat) float64 {
	curTotalTime := curCpuStat.SoftirqTime + curCpuStat.IrqTime + curCpuStat.IowaitTime + curCpuStat.IdleTime + curCpuStat.SystemTime + curCpuStat.NiceTime + curCpuStat.UserTime
	prevTotalTime := prevCpuStat.SoftirqTime + prevCpuStat.IrqTime + prevCpuStat.IowaitTime + prevCpuStat.IdleTime + prevCpuStat.SystemTime + prevCpuStat.NiceTime + prevCpuStat.UserTime

	curIdleTime := curCpuStat.IdleTime
	prevIdleTime := prevCpuStat.IdleTime

	//fmt.Println("curTotalTime")
	//fmt.Println(curTotalTime)
	//fmt.Println("prevTotalTime")
	//fmt.Println(prevTotalTime)
	//fmt.Println("curIdleTime")
	//fmt.Println(curIdleTime)
	//fmt.Println("prevIdleTime")
	//fmt.Println(prevIdleTime)

	idleTicks := float64(curIdleTime - prevIdleTime)
	totalTicks := float64(curTotalTime - prevTotalTime)


	//fmt.Println("idleTicks")
	//fmt.Println(idleTicks)
	//fmt.Println("totalTicks")
	//fmt.Println(totalTicks)


	cpuUsage := 100 * (totalTicks - idleTicks)/totalTicks

	//fmt.Println("cpuUsage")
	//fmt.Println(cpuUsage)

	return cpuUsage
}